package main

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/redmask-hb/GoSimplePrint/goPrint"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type zbxInterface interface {
	flushHostMap()												//刷新加载hostMap
	getHostItem(groupID string) *ItemRst	 					//获取主机组监控数据
	getGroupID(groupName string) string							//获取主机组id
	handleData(item *ItemRst, itemInfo *map[string]*hostItem)	//处理获得组的json数据
	getEvents(envts *map[string]int, EvntsClock *map[string][]string)
	getTemplate() map[string]string	  //获取模板id 返回值为: map["模板name"]="tempID"
	getHistory(itemid string) string
	/* 创建主机
		传参列表-> 可见名称， hostname名称(agent配置文件里的那个), agentIP(主动模式填0.0.0.0即可), 主机组id, 端口号默认10050(主动模式填0)
		链接哪些模板接收一个切片，接收一个 tempID切片，
	 */
	createHost(VisibleName, hostname, agentIP, groupID, port string, tempID []string) (string, error)
	deleteHost(hostArr []string) (err error)  	//删除主机，接收一个切片，批量删除主机
	tgProblem()								    //检索在故障状态并且没确认的触发器
	Close() error //关闭网络链接资源
}

type Zobj struct {
	token, url, user, passwd string
	hostMap	map[string]*hostInfo
	request 	*http.Request
	client		*http.Client
	resp        *http.Response
}

// NewObj 创建一个实例, 初始化
func NewObj(url, user, passwd string, bar *goPrint.Bar) *Zobj {
	bar.SetNotice("获取数据: ")
	bar.SetGraph("*")
	SendStr := map[string]interface{}{
		"jsonrpc": "2.0",
		"method": "user.login",
		"params": map[string]interface{}{
			"user": user,
			"password": passwd,
		},
		"id": 1,
	}
	JsonTmp, err := json.Marshal(SendStr)
	if err != nil {log.Panic(err)}
	bar.PrintBar(1)
	myZobj := new(Zobj)
	myZobj.request, err = http.NewRequest("POST", url, bytes.NewBuffer(JsonTmp))
	if err != nil {log.Panic(err)}
	myZobj.request.Header.Add("Content-Type", "application/json")
	myZobj.client = &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
	myZobj.resp, err = myZobj.client.Do(myZobj.request)
	if err != nil {log.Panic(err)}
	body, err := ioutil.ReadAll(myZobj.resp.Body)
	bar.PrintBar(2)
	if err != nil {log.Panic(err)}
	type rst struct {Result string `json:"result"`; Error interface{} `json:"error"`}
	token := new(rst)
	err = json.Unmarshal(body, token)
	if err != nil {log.Panic(err)}
	if token.Error != nil {log.Println(token.Error); os.Exit(2)}
	myZobj.url=url; myZobj.token=token.Result; myZobj.user=user; myZobj.passwd=passwd
	bar.PrintBar(3)
	bar.PrintEnd()
	return myZobj
}

func (Z *Zobj) flushHostMap(bar *goPrint.Bar) {
	bar = goPrint.NewBar(3)
	bar.SetNotice("正在处理数据: ")
	bar.SetGraph("*")
	SendStr := map[string]interface{}{
		"jsonrpc": "2.0",
		"method": "host.get",
		"params": map[string]interface{}{
			"output": []string{"hostid", "name", "ip", "host"},
			"selectGroups": "extend",
			"selectInterfaces": []string{"interfaces", "ip"},
		},
		"auth": Z.token,
		"id": 1,
	}
	JsonTmp, err := json.Marshal(SendStr)
	bar.PrintBar(1)
	if err != nil {log.Panic(err)}
	Z.request, err = http.NewRequest("POST", Z.url, bytes.NewBuffer(JsonTmp))
	if err != nil {log.Panic(err)}
	Z.request.Header.Add("Content-Type", "application/json")
	Z.resp, err = Z.client.Do(Z.request)
	if err != nil {log.Panic(err)}
	body, err := ioutil.ReadAll(Z.resp.Body)
	if err != nil {log.Panic(err)}
	type rst struct {
		Result []struct {
			Hostid string `json:"hostid"`
			Name string	`json:"name"`
			Host string `json:"host"`
			Groups []struct {
				Groupid string `json:"groupid"`
				Name string `json:"name"`
			} `json:"groups"`
			Interfaces []struct{
				Ip	string	`json:"ip"`
			} `json:"interfaces"`
		} `json:"result"`
	}
	tmpRst := new(rst)
	json.Unmarshal(body, tmpRst)
	bar.PrintBar(2)
	Z.hostMap = make(map[string]*hostInfo)
	for _, ve := range tmpRst.Result {
		if len(ve.Interfaces) == 0 {
			Z.hostMap[ve.Hostid] = &hostInfo{hostName: ve.Name, GroupID: ve.Groups[0].Groupid, SN: ve.Host}
			continue
		}
		Z.hostMap[ve.Hostid] = &hostInfo{hostName: ve.Name, GroupID: ve.Groups[0].Groupid, ip: ve.Interfaces[0].Ip, SN: ve.Host}
	}
	bar.PrintBar(3)
}

func (Z *Zobj) getEvents(envts *map[string]int, EvntsClock *map[string][]string, bar *goPrint.Bar) {
	regx := regexp.MustCompile(`重启`)
	SendStr := map[string]interface{}{
		"jsonrpc": "2.0",
		"method": "event.get",
		"params": map[string]interface {}{
			"output": []string{"clock", "name"},
			"selectHosts": "hostid",
			"object": 0,
			"source": 0,
			"severity": 2,
			"time_from": fmt.Sprintf("%s", strconv.FormatInt(time.Now().AddDate(0, 0, -7).Unix(),10)),
			"time_till": fmt.Sprintf("%s", strconv.FormatInt(time.Now().Unix(), 10)),
			"sortfield": "clock",
			"sortorder": "ASC",
		},
		"auth": Z.token,
		"id": 1,
	}
	JsonTmp, err := json.Marshal(SendStr)
	if err != nil {log.Panic(err)}
	Z.request, err = http.NewRequest("POST", Z.url, bytes.NewBuffer(JsonTmp))
	if err != nil {log.Panic(err)}
	Z.request.Header.Add("Content-Type", "application/json")
	Z.resp, err = Z.client.Do(Z.request)
	if err != nil {log.Panic(err)}
	body, err := ioutil.ReadAll(Z.resp.Body)
	type rst struct {
		Result []struct {
			Name string `json:"name"`
			Clock string `json:"clock"`
			Hosts []struct{
				Hostid string `json:"hostid"`
			} `json:"hosts"`
		} `json:"result"`
	}
	tmpRst := new(rst)
	err = json.Unmarshal(body, tmpRst)
	length :=len(tmpRst.Result)<<1
	bar = goPrint.NewBar(length)
	bar.SetNotice("正在处理数据: ")
	bar.SetGraph("*")
	i:=1
	for _, hostid := range tmpRst.Result{
		bar.PrintBar(i)
		i++
		if len(hostid.Hosts) == 0 {continue}
		(*EvntsClock)[hostid.Hosts[0].Hostid] = make([]string, 0)
	}
	for _, hostid := range tmpRst.Result {
		bar.PrintBar(i)
		i++
		if regx.MatchString(hostid.Name) {
			if len(hostid.Hosts) == 0 {continue}
			(*envts)[hostid.Hosts[0].Hostid]++
			//cloK, _:= strconv.ParseInt(hostid.Clock, 10, 64)
			(*EvntsClock)[hostid.Hosts[0].Hostid] =  append((*EvntsClock)[hostid.Hosts[0].Hostid], hostid.Clock)
		}
	}
	bar.PrintEnd()
}

func (Z *Zobj) Close() error {
	if err := Z.resp.Body.Close(); err != nil {return err}
	if err := Z.request.Body.Close(); err != nil {return err}
	return nil
}
func (Z *Zobj) getGroupID(groupName string) string {
	SendStr := map[string]interface{}{
		"jsonrpc": "2.0",
		"method": "hostgroup.get",
		"params": map[string]interface{}{
			"output": "groupid",
			"filter": map[string][]string{"name": {groupName}},
		},
		"auth": Z.token,
		"id": 1,
	}
	JsonTmp, err := json.Marshal(SendStr)
	if err != nil {log.Panic(err)}
	Z.request, err = http.NewRequest("POST", Z.url, bytes.NewBuffer(JsonTmp))
	if err != nil {log.Panic(err)}
	Z.request.Header.Add("Content-Type", "application/json")
	Z.resp, err = Z.client.Do(Z.request)
	if err != nil {log.Panic(err)}
	body, err := ioutil.ReadAll(Z.resp.Body)
	if err != nil {log.Panic(err)}
	type rst struct {
		Result []struct {
			Groupid string `json:"groupid"`
		} `json:"result"`
	}
	tmpRst := new(rst)
	json.Unmarshal(body, tmpRst)
	if len(tmpRst.Result) == 0 {return fmt.Sprintf("yml配置主机组的名错误：%s, 不存在该主机组", groupName)}
	return tmpRst.Result[0].Groupid
}

func (Z *Zobj) getHostItem(groupID string) *ItemRst {
	SendStr := map[string]interface{}{
		"jsonrpc": "2.0",
		"method": "item.get",
		"params": map[string]interface{}{
			"output": []string{"key_", "lastvalue", "hostid", "itemid"},
			"groupids": groupID,
			"search": map[string][]interface{}{
				"key_": {
					"system.uptime",              //系统开机时长
					"vfs.fs.size[/,total]",	      //磁盘总大小
					"vfs.fs.size[/,used]",	      //磁盘使用
					"vfs.fs.size[/home,total]",  //data1磁盘总大小
					"vfs.fs.size[/home,used]",   //data1磁盘使用率
					"system.cpu.load[all,avg1]",
					"system.cpu.load[all,avg5]",
					"system.cpu.load[all,avg15]",
					"Pcheck.version",
					"system.cpu.num",
					"system.cpu.util[,idle]",     // cpu空闲时间
					"vm.memory.utilization",      // 内存使用率
					"vm.memory.size[total]",      // 内存总大小
					"vm.memory.size[available]",  // 可用内存
				},
			},
			"searchByAny": "true",
			"sortfield": "name",
		},
		"auth": Z.token,
		"id": 1,
	}
	JsonTmp, err := json.Marshal(SendStr)
	if err != nil {log.Panic(err)}
	Z.request, err = http.NewRequest("POST", Z.url, bytes.NewBuffer(JsonTmp))
	if err != nil {log.Panic(err)}
	Z.request.Header.Add("Content-Type", "application/json")
	Z.resp, err = Z.client.Do(Z.request)
	if err != nil {log.Panic(err)}
	body, err := ioutil.ReadAll(Z.resp.Body)
	if err != nil {log.Panic(err)}
	tmpRst := new(ItemRst)
	if err = json.Unmarshal(body, tmpRst); err != nil {return nil}
	return tmpRst
}

func (Z *Zobj) handleData(item *ItemRst, itemInfo *map[string]*hostItem, envts *map[string]int, groupName string, EvntsClock *map[string][]string) {
	lenght, ive:=len(item.Result), 1
	bar:=goPrint.NewBar(lenght)
	timeLayout := "2006-01-02 15:04:05"
	bar.SetNotice(groupName+": ")
	bar.SetGraph("*")
	bar.SetNoticeColor(goPrint.FontColor.Blue)
	for _, ve := range item.Result {(*itemInfo)[ve.HostID] = new(hostItem)}
	for _, ve := range item.Result {
		bar.PrintBar(ive)
		ive++
		if ve.Key == "vm.memory.size[available]" {
			i := strings.IndexRune(ve.Lastvalue, '.')
			if i != -1 {ve.Lastvalue = ve.Lastvalue[:i]}
			MemAva, _ := strconv.ParseInt(ve.Lastvalue, 10, 64)
			(*itemInfo)[ve.HostID].MEMA = MemAva
			(*itemInfo)[ve.HostID].memAvailable = formatFileSize(MemAva)
		}
		if ve.Key == "vm.memory.size[total]" {
			i := strings.IndexRune(ve.Lastvalue, '.')
			if i != -1 {ve.Lastvalue = ve.Lastvalue[:i]}
			MemTol, _ := strconv.ParseInt(ve.Lastvalue, 10, 64)
			(*itemInfo)[ve.HostID].MEMT = MemTol
			(*itemInfo)[ve.HostID].memTotal = formatFileSize(MemTol)
		}
		if ve.Key == "system.uptime" {
			(*itemInfo)[ve.HostID].hostIP, (*itemInfo)[ve.HostID].hostname = Z.hostMap[ve.HostID].ip, Z.hostMap[ve.HostID].hostName
			(*itemInfo)[ve.HostID].SN = Z.hostMap[ve.HostID].SN
			daytime, _ := strconv.ParseFloat(ve.Lastvalue,64)
			(*itemInfo)[ve.HostID].uptime = fmt.Sprintf("%.2f 天", resolveTime(daytime))
		}
		if ve.Key == "vfs.fs.size[/,total]" {
			diskRootTotal ,_ := strconv.ParseInt(ve.Lastvalue, 10, 64)
			(*itemInfo)[ve.HostID].DT = diskRootTotal
			(*itemInfo)[ve.HostID].diskRootSize = formatFileSize(diskRootTotal)
		}
		if ve.Key == "vfs.fs.size[/home,total]" {
			diskDaTotal ,_ := strconv.ParseInt(ve.Lastvalue, 10, 64)
			(*itemInfo)[ve.HostID].DAT = diskDaTotal
			(*itemInfo)[ve.HostID].diskData1Size = formatFileSize(diskDaTotal)
		}
		if ve.Key == "vfs.fs.size[/,used]" {
			diskRootUsed ,_ := strconv.ParseInt(ve.Lastvalue, 10, 64)
			(*itemInfo)[ve.HostID].DU = diskRootUsed
			(*itemInfo)[ve.HostID].diskRootused = formatFileSize(diskRootUsed)
		}
		if ve.Key == "vfs.fs.size[/home,used]" {
			diskDu ,_ := strconv.ParseInt(ve.Lastvalue, 10, 64)
			(*itemInfo)[ve.HostID].DAU = diskDu
			(*itemInfo)[ve.HostID].diskData1used = formatFileSize(diskDu)
		}
		if ve.Key == "system.cpu.util[,idle]" {
			i := strings.IndexRune(ve.Lastvalue, '.')
			if i != -1 {ve.Lastvalue = ve.Lastvalue[:i]}
			cpuIdle, _:= strconv.ParseInt(ve.Lastvalue, 10, 64)
			(*itemInfo)[ve.HostID].cpuUsed = fmt.Sprintf("%d", 100-cpuIdle) + "%"
		}
		if ve.Key == "system.cpu.num" {
			(*itemInfo)[ve.HostID].cpuNum = ve.Lastvalue
		}
		if ve.Key == "Pcheck.version" {
			(*itemInfo)[ve.HostID].version = ve.Lastvalue
			if (*itemInfo)[ve.HostID].version == ""  {
				(*itemInfo)[ve.HostID].version, (*itemInfo)[ve.HostID].isOk = Z.getHistory(ve.Itemid), true
			}
		}
	}
	for hostid, info := range *itemInfo {
		(*itemInfo)[hostid].memUsed =  fmt.Sprintf("%.2f", float64(info.MEMT - info.MEMA)/float64(info.MEMT)*100) + "%"
		(*itemInfo)[hostid].diskRoot = fmt.Sprintf("%.2f", float64(info.DU)/float64(info.DT)*100) + "%"
		if (*itemInfo)[hostid].diskData1Size != "" {
			(*itemInfo)[hostid].diskData1 = fmt.Sprintf("%.2f", float64(info.DAU)/float64(info.DAT)*100) + "%"
		}
		//if (*itemInfo)[hostid].cpuNum == "0" {
			//delete(*itemInfo, hostid)
			//continue
//		}
		if (*envts)[hostid] != 0 && (*envts)[hostid] != 1  {
			(*itemInfo)[hostid].HostRestartCount = (*envts)[hostid]/2
			i:=0
			for _, clock := range (*EvntsClock)[hostid] {
				cloK, _:= strconv.ParseInt(clock, 10, 64)
				if i % 2 == 0 {
					(*itemInfo)[hostid].clock = fmt.Sprintf("%s --> 🔄\\n%s",
						time.Unix(cloK, 0).Format(timeLayout),
						(*itemInfo)[hostid].clock)
				}
				i++
			}
		} else {
			(*itemInfo)[hostid].HostRestartCount = (*envts)[hostid]
			if len ((*EvntsClock)[hostid]) == 0 {continue}
			cloK, _:= strconv.ParseInt((*EvntsClock)[hostid][0], 10, 64)
			(*itemInfo)[hostid].clock = fmt.Sprintf("%s --> 🔄", time.Unix(cloK, 0).Format(timeLayout))
		}
	}
	bar.PrintEnd()
}

func (Z *Zobj) getHistory(itemid string) string {
	SendStr := map[string]interface{}{
		"jsonrpc": "2.0",
		"method": "history.get",
		"params": map[string]interface{}{
			"output": []string{"value"},
			"history": 1,
			"itemids": itemid,
			"limit": 1,
		},
		"auth": Z.token,
		"id": 1,
	}
	JsonTmp, err := json.Marshal(SendStr)
	if err != nil {log.Panic(err)}
	Z.request, err = http.NewRequest("POST", Z.url, bytes.NewBuffer(JsonTmp))
	if err != nil {log.Panic(err)}
	Z.request.Header.Add("Content-Type", "application/json")
	Z.resp, err = Z.client.Do(Z.request)
	if err != nil {log.Panic(err)}
	body, err := ioutil.ReadAll(Z.resp.Body)
	if err != nil {log.Panic(err)}
	type rst struct {
		Result []struct {
			Value string `json:"value"`
		} `json:"result"`
	}
	tmpRst := new(rst)
	err = json.Unmarshal(body, tmpRst)
	return tmpRst.Result[0].Value
}

func (Z *Zobj) getTemplate() map[string]string {
	SendStr := map[string]interface{}{
		"jsonrpc": "2.0",
		"method": "template.get",
		"params": map[string][]string{
			"output": {
				"name",
				"templateid",
			},
		},
		"auth": Z.token,
		"id": 1,
	}
	JsonTmp, err := json.Marshal(SendStr)
	if err != nil {log.Panic(err)}
	Z.request, err = http.NewRequest("POST", Z.url, bytes.NewBuffer(JsonTmp))
	if err != nil {log.Panic(err)}
	Z.request.Header.Add("Content-Type", "application/json")
	Z.resp, err = Z.client.Do(Z.request)
	if err != nil {log.Panic(err)}
	body, err := ioutil.ReadAll(Z.resp.Body)
	if err != nil {log.Panic(err)}
	type rst struct {Result []map[string]string `json:"result"`}
	tmpRst := new(rst)
	json.Unmarshal(body, tmpRst)
	ans := make(map[string]string)
	for _, Map := range tmpRst.Result {
		ans[Map["name"]]=Map["templateid"]
	}
	return ans
}

func (Z *Zobj) createHost(VisibleName, hostname, agentIP, groupID, port string, tempID []string) (string, error) {
	temp := make([]map[string]string, len(tempID))
	for i, ve := range tempID {
		temp[i]= make(map[string]string)
		temp[i]["templateid"]=ve
	}
	SendStr := map[string]interface{}{
		"jsonrpc": "2.0",
		"method": "host.create",
		"params": map[string]interface{}{
			"host": hostname,
			"name": VisibleName,
			"interfaces": []map[string]interface{}{
				{"type": 1, "main": 1, "useip": 1, "ip": agentIP, "dns": "", "port": port},
			},
			"groups": []map[string]interface{}{
				{"groupid": groupID},
			},
			"templates": temp,
		},
		"auth": Z.token,
		"id": 1,
	}
	JsonTmp, err := json.Marshal(SendStr)
	if err != nil {log.Panic(err)}
	Z.request, err = http.NewRequest("POST", Z.url, bytes.NewBuffer(JsonTmp))
	if err != nil {log.Panic(err)}
	Z.request.Header.Add("Content-Type", "application/json")
	Z.resp, err = Z.client.Do(Z.request)
	if err != nil {log.Panic(err)}
	body, err := ioutil.ReadAll(Z.resp.Body)
	if err != nil {log.Panic(err)}
	type rst struct {Error map[string]string `json:"error"`; Result struct {Hostids []string `json:"hostids"`} `json:"result"`}
	tmpRst := new(rst)
	err = json.Unmarshal(body, tmpRst)
	if tmpRst.Error != nil {
		return "",errors.New(tmpRst.Error["data"])
	}
	return tmpRst.Result.Hostids[0],nil
}
func (Z *Zobj) deleteHost(hostArr []string) (err error) {
	SendStr := map[string]interface{}{
		"jsonrpc": "2.0",
		"method": "host.delete",
		"params": hostArr,
		"auth": Z.token,
		"id": 1,
	}
	JsonTmp, err := json.Marshal(SendStr)
	if err != nil {log.Panic(err)}
	Z.request, err = http.NewRequest("POST", Z.url, bytes.NewBuffer(JsonTmp))
	if err != nil {log.Panic(err)}
	Z.request.Header.Add("Content-Type", "application/json")
	Z.resp, err = Z.client.Do(Z.request)
	if err != nil {log.Panic(err)}
	body, err := ioutil.ReadAll(Z.resp.Body)
	if err != nil {log.Panic(err)}
	type rst struct {Error map[string]string `json:"error"`}
	tmpRst := new(rst)
	err = json.Unmarshal(body, tmpRst)
	if tmpRst.Error != nil {
		return errors.New(tmpRst.Error["data"])
	}
	return nil
}

func (Z *Zobj) tgProblem() {
	SendStr := map[string]interface{}{
		"jsonrpc": "2.0",
		"method": "trigger.get",
		"params": map[string]interface{}{
			//"output": []string{"triggerid", "description", "priority"},
			"filter": map[string]int{"value": 1},
			"sortfield": "priority",	//排序
			"sortorder": "DESC",
			"min_severity": 2,			//返回指定告警级别的告警，这里是大于等于告警
			"skipDependent": 1,			//跳过依赖于其他问题中的触发器
			"monitored": 1,				//属于受监控主机的已启用触发器，并仅包含已启用的项目
			"active":1,					//只返回属于受监控主机的启用的触发器（与上条意思差不多
			"expandDescription":1,		//在触发器的名称中展开宏
			"selectHosts":[]string{"name"},	//在结果中返回关联的主机信息
			"selectGroups":[]string{"name"}, //在结果中返回关联的主机组信息
			//"only_true": 1,					//只返回最近处于问题状态的触发器
			"withLastEventUnacknowledged": 1,  //只返回没有确认的问题
		},
		"auth": Z.token,
		"id": 1,
	}
	JsonTmp, err := json.Marshal(SendStr)
	if err != nil {log.Panic(err)}
	Z.request, err = http.NewRequest("POST", Z.url, bytes.NewBuffer(JsonTmp))
	if err != nil {log.Panic(err)}
	Z.request.Header.Add("Content-Type", "application/json")
	Z.resp, err = Z.client.Do(Z.request)
	if err != nil {log.Panic(err)}
	body, err := ioutil.ReadAll(Z.resp.Body)
	//fmt.Println(string(body))
	type rst struct {
		Result []struct {
			Triggerid string `json:"triggerid"`
			Description string `json:"description"`
			Priority string `json:"priority"`
		} `json:"result"`
	}
	tmpRst := new(rst)
	err = json.Unmarshal(body, tmpRst)
	fmt.Println(tmpRst.Result)
}